﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Diagnostics;
using System.Drawing;

namespace Opennav.MapGenerator.Map.MapTiles
{
    public class MapTileSubfile
    {
        public static byte LAST_LEVEL_FLAG = 0x80;

        private List<MapObject> objects = new List<MapObject>();
        private Zoom[] _zoomLevels = null;

        public void Add(MapObject obj)
        {
            objects.Add(obj);
        }

        public void Remove(MapObject obj)
        {
            objects.Remove(obj);
        }

        public Zoom[] ZoomLevels 
        {
            get { return _zoomLevels; }
            set { this._zoomLevels = value; }
        }

        public void Write(Stream stream)
        {
            Console.Write("Writing map tiles ... ");
            Debug.Assert(ZoomLevels != null);

            string indexFile = Path.GetTempFileName();
            string tileFile = Path.GetTempFileName();
            string zoomFile = Path.GetTempFileName();

            FileStream indexStream = new FileStream(indexFile, FileMode.Open, FileAccess.ReadWrite);
            FileStream tileStream = new FileStream(tileFile, FileMode.Open, FileAccess.ReadWrite);
            FileStream zoomStream = new FileStream(zoomFile, FileMode.Open, FileAccess.ReadWrite);

            List<MapTile> prevtiles = null;
            int tileId = 0;
            foreach (Zoom zoom in _zoomLevels)
            {
                List<MapTile> nexttiles = new List<MapTile>();
                if (prevtiles == null) 
                {
                    foreach (MapTile tile in MapTile.Create(objects, zoom)) 
                    {
                        tile.Id = (ushort)tileId++;
                        nexttiles.Add(tile);
                        tile.Offset = (int)tileStream.Position;
                        tile.WriteTile(tileStream);
                        Console.Write("\rWriting map tiles ... " + tileId);
                        zoom.TileCount++;
                    }
                    nexttiles.Last().Last = true;
                } 
                else 
                {
                    foreach (MapTile prev in prevtiles) 
                    {
                        prev.SubtileId = (ushort)tileId;
                        prev.WriteIndex(indexStream);
                        foreach (MapTile tile in MapTile.CreateLevel(prev, zoom)) 
                        {
                            tile.Id = (ushort)tileId++;
                            nexttiles.Add(tile);
                            tile.Offset = (int)tileStream.Position;
                            tile.WriteTile(tileStream);
                            Console.Write("\rWriting map tiles ... " + tileId);
                            zoom.TileCount++;
                        }
                        nexttiles.Last().Last = true;
                    }
                }
                prevtiles = nexttiles;
            }
            Console.WriteLine();
            foreach (MapTile prevtile in prevtiles)
            {
                prevtile.WriteIndex(indexStream);
            }
            BinaryWriter zoomWriter = new BinaryWriter(zoomStream);
            foreach (Zoom zoomLevel in ZoomLevels)
            {
                byte resolution = (byte)zoomLevel.Resolution;
                ushort tileCount = (ushort)zoomLevel.TileCount;
                zoomWriter.Write(resolution);
                zoomWriter.Write(tileCount);
            }
            int tilesOffset = (int)(4 + zoomStream.Length + indexStream.Length);

            int zoomOffset = 0;
            int tileDefOffset = 0;
            int tileOffset = 0;
            int offset = (int)stream.Position;
            BinaryWriter writer = new BinaryWriter(stream);
            writer.Write(zoomOffset);
            writer.Write((byte)ZoomLevels.Length);
            writer.Write(tileDefOffset);
            writer.Write((ushort)tileId);
            writer.Write(tileOffset);
            zoomOffset = (int)stream.Position - offset;
            zoomStream.Seek(0, SeekOrigin.Begin);
            zoomStream.CopyTo(stream);
            tileDefOffset = (int)stream.Position - offset;
            indexStream.Seek(0, SeekOrigin.Begin);
            indexStream.CopyTo(stream);
            tileOffset = (int)stream.Position - offset;
            tileStream.Seek(0, SeekOrigin.Begin);
            tileStream.CopyTo(stream);
            int endOffset = (int)stream.Position;
            stream.Seek(offset, SeekOrigin.Begin);
            writer.Write(zoomOffset);
            writer.Write((byte)ZoomLevels.Length);
            writer.Write(tileDefOffset);
            writer.Write((ushort)tileId);
            writer.Write(tileOffset);
            stream.Seek(endOffset, SeekOrigin.Begin);

            indexStream.Close();
            File.Delete(indexFile);
            tileStream.Close();
            File.Delete(tileFile);
            zoomStream.Close();
            File.Delete(zoomFile);
        }
    }
}
